package com.zck.thumbup.service.impl;

import com.zck.thumbup.constant.ThumbConstant;
import com.zck.thumbup.entity.Blog;
import com.zck.thumbup.entity.Thumb;
import com.zck.thumbup.entity.User;
import com.zck.thumbup.exception.BusinessException;
import com.zck.thumbup.exception.ErrorCode;
import com.zck.thumbup.lock.RedisLock;
import com.zck.thumbup.mapper.ThumbMapper;
import com.zck.thumbup.model.dto.thumb.DoThumbRequest;
import com.zck.thumbup.service.BlogService;
import com.zck.thumbup.service.ThumbService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zck.thumbup.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 点赞服务实现类 - 使用@RedisLock注解方式
 */
@Service("thumbServiceDB")
@Slf4j
@RequiredArgsConstructor
public class ThumbServiceImpl extends ServiceImpl<ThumbMapper, Thumb> implements ThumbService {

    private final UserService userService;
    private final BlogService blogService;
    private final TransactionTemplate transactionTemplate;
    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 点赞操作 - 使用@RedisLock注解
     * 锁key包含博客ID，防止同一博客并发点赞
     */
    @Override
    @RedisLock(key = "lock:thumb:blog:#doThumbRequest.blogId",
            waitTime = 3,
            leaseTime = 30,
            timeUnit = TimeUnit.SECONDS)
    public Boolean doThumbByRedis(DoThumbRequest doThumbRequest, HttpServletRequest request) {
        // 参数校验
        validateThumbRequest(doThumbRequest);

        User loginUser = userService.getLoginUser(request);
        Long blogId = doThumbRequest.getBlogId();
        Long userId = loginUser.getId();

        // 编程式事务执行点赞操作
        return transactionTemplate.execute(status -> {
            try {
                return executeThumbOperation(blogId, userId);
            } catch (BusinessException e) {
                status.setRollbackOnly();
                throw e;
            } catch (Exception e) {
                status.setRollbackOnly();
                log.error("点赞操作异常: userId={}, blogId={}", userId, blogId, e);
                throw new BusinessException(ErrorCode.SYSTEM_ERROR,"点赞失败，请重试");
            }
        });
    }

    /**
     * 取消点赞操作 - 使用@RedisLock注解
     */
    @Override
    @RedisLock(key = "lock:thumb:blog:#doThumbRequest.blogId",
            waitTime = 3,
            leaseTime = 30,
            timeUnit = TimeUnit.SECONDS)
    public Boolean undoThumbByRedis(DoThumbRequest doThumbRequest, HttpServletRequest request) {
        validateThumbRequest(doThumbRequest);

        User loginUser = userService.getLoginUser(request);
        Long blogId = doThumbRequest.getBlogId();
        Long userId = loginUser.getId();

        return transactionTemplate.execute(status -> {
            try {
                return executeUndoThumbOperation(blogId, userId);
            } catch (BusinessException e) {
                status.setRollbackOnly();
                throw e;
            } catch (Exception e) {
                status.setRollbackOnly();
                log.error("取消点赞操作异常: userId={}, blogId={}", userId, blogId, e);
                throw new BusinessException(ErrorCode.SYSTEM_ERROR,"取消点赞失败，请重试");
            }
        });
    }

    /**
     * 执行点赞操作
     */
    private Boolean executeThumbOperation(Long blogId, Long userId) {
        // 1. 检查是否已点赞（双重检查）
        if (checkThumbExists(blogId, userId)) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"用户已点赞");
        }

        // 2. 更新博客点赞数
        boolean updateSuccess = updateBlogThumbCount(blogId, 1);
        if (!updateSuccess) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"博客不存在或更新失败");
        }

        // 3. 创建点赞记录
        Thumb thumb = createThumb(blogId, userId);
        boolean saveSuccess = this.save(thumb);

        if (saveSuccess) {
            // 4. 更新缓存
            cacheThumbRecord(blogId, userId, thumb.getId());
            log.info("点赞成功: userId={}, blogId={}", userId, blogId);
        }

        return saveSuccess;
    }

    /**
     * 执行取消点赞操作
     */
    private Boolean executeUndoThumbOperation(Long blogId, Long userId) {
        // 1. 获取点赞记录ID
        Long thumbId = getThumbId(blogId, userId);
        if (thumbId == null) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"用户未点赞");
        }

        // 2. 更新博客点赞数
        boolean updateSuccess = updateBlogThumbCount(blogId, -1);
        if (!updateSuccess) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"博客不存在或更新失败");
        }

        // 3. 删除点赞记录
        boolean deleteSuccess = this.removeById(thumbId);

        if (deleteSuccess) {
            // 4. 清理缓存
            clearThumbCache(blogId, userId);
            log.info("取消点赞成功: userId={}, blogId={}", userId, blogId);
        }

        return deleteSuccess;
    }

    /**
     * 检查是否已点赞（双重检查）
     */
    private boolean checkThumbExists(Long blogId, Long userId) {

        // 先查Redis缓存
        String redisKey = ThumbConstant.USER_THUMB_KEY_PREFIX + userId;
        boolean existsInRedis = redisTemplate.opsForHash().hasKey(redisKey, blogId.toString());
        if (existsInRedis) {
            return true;
        }

        // 再查数据库
        Thumb thumb = this.lambdaQuery()
                .eq(Thumb::getBlogId, blogId)
                .eq(Thumb::getUserId, userId)
                .one();
        if(thumb==null){
            return false;
        }
        //写入到Redis中
        cacheThumbRecord(blogId, userId, thumb.getId());
        return true;
    }

    /**
     * 获取点赞记录ID
     */
    private Long getThumbId(Long blogId, Long userId) {
        // 先查Redis缓存
        String redisKey = ThumbConstant.USER_THUMB_KEY_PREFIX + userId;
        Object thumbIdObj = redisTemplate.opsForHash().get(redisKey, blogId.toString());

        if (thumbIdObj != null) {
            return Long.valueOf(thumbIdObj.toString());
        }

        // 缓存不存在，查数据库
        Thumb thumb = this.lambdaQuery()
                .eq(Thumb::getBlogId, blogId)
                .eq(Thumb::getUserId, userId)
                .one();

        if (thumb != null) {
            // 重建缓存
            cacheThumbRecord(blogId, userId, thumb.getId());
            return thumb.getId();
        }

        return null;
    }

    /**
     * 更新博客点赞数
     */
    private boolean updateBlogThumbCount(Long blogId, int delta) {
        String sql = delta > 0 ? "thumbCount = thumbCount + 1" : "thumbCount = thumbCount - 1";
        return blogService.lambdaUpdate()
                .eq(Blog::getId, blogId)
                .setSql(sql)
                .ge(Blog::getThumbCount, 0) // 防止点赞数变为负数
                .update();
    }

    /**
     * 创建点赞记录
     */
    private Thumb createThumb(Long blogId, Long userId) {
        Thumb thumb = new Thumb();
        thumb.setBlogId(blogId);
        thumb.setUserId(userId);
        thumb.setCreateTime(LocalDateTime.now());
        return thumb;
    }

    /**
     * 缓存点赞记录
     */
    private void cacheThumbRecord(Long blogId, Long userId, Long thumbId) {
        try {
            String redisKey = ThumbConstant.USER_THUMB_KEY_PREFIX + userId;

            // 获取博客信息计算过期时间
            Blog blog = blogService.getById(blogId);
            if (blog == null) {
                log.warn("博客不存在，使用默认过期时间: blogId={}", blogId);
                redisTemplate.expire(redisKey, 30, TimeUnit.DAYS);
                return;
            }

            LocalDateTime expireTime = blog.getPubTime().plusMonths(1);
            // 确保过期时间在未来
            if (expireTime.isBefore(LocalDateTime.now())) {
                expireTime = LocalDateTime.now().plusMonths(1);
            }

            Instant expireInstant = expireTime.atZone(ZoneId.systemDefault()).toInstant();

            redisTemplate.opsForHash().put(redisKey, blogId.toString(), thumbId);
            redisTemplate.expireAt(redisKey, Date.from(expireInstant));

            log.debug("点赞缓存设置成功: userId={}, blogId={}, 过期时间={}", userId, blogId, expireTime);

        } catch (Exception e) {
            log.error("缓存点赞记录失败: userId={}, blogId={}", userId, blogId, e);
            // 缓存失败不影响主流程
        }
    }

    /**
     * 清理点赞缓存
     */
    private void clearThumbCache(Long blogId, Long userId) {
        try {
            String redisKey = ThumbConstant.USER_THUMB_KEY_PREFIX + userId;
            redisTemplate.opsForHash().delete(redisKey, blogId.toString());
            log.debug("清理点赞缓存成功: userId={}, blogId={}", userId, blogId);
        } catch (Exception e) {
            log.error("清理点赞缓存失败: userId={}, blogId={}", userId, blogId, e);
        }
    }

    /**
     * 参数校验
     */
    private void validateThumbRequest(DoThumbRequest doThumbRequest) {
        if (doThumbRequest == null || doThumbRequest.getBlogId() == null) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"参数错误");
        }
    }

    /**
     * 检查是否已点赞（对外接口）
     */
    @Override
    public Boolean hasThumb(Long blogId, Long userId) {
        if (blogId == null || userId == null) {
            return false;
        }
        return checkThumbExists(blogId, userId);
    }
}